在混亂的背景下,1987 年,任職於 Xerox 的 Joe Becker、Lee Collins 和任職於 Apple 的 Mark Davis 萌生了統一字元編碼的想法並開始傳教,促使當時電腦界的大佬們(例如 Microsoft、IBM、昇陽)成立了 Unicode 聯盟(Unicode Consortium),希望能解決目前各國文字編碼標準不一的問題。
起初,整合小組將 ASCII 失敗的原因歸咎為擴充性不足——1 Byte、共 8 bit、總計 256 個格子實在太少了,不如改用 2 個 Byte 共 16 bit,這樣就能放下 2^16 = 65,536 個字符了,這個數字已經是原本容量的 256 倍,用來收錄文字應該已經很夠了!
——嗎?
1991 年,Unicode 1.0 正式釋出,其中共收錄了 24 種書寫文字、共 7,129 個字符。當時漢字的整理還沒結束,所以這個最初的版本並沒有收錄漢字。
不過很快地,東亞各國不斷提交的新的漢字來源,眾多的學術與文化單位打算將歷史文本給電子化,希望連已經消亡的古代文字都收錄進來。面對大家爭先恐後提交、深怕晚了就沒位置放的態度,Unicode 聯盟發現他們輕忽了工作所需的數量級。
於是,在不改動原本規劃的 65,536 格子——後來這一區被稱作第 0 基本平面(Basic Multilingual Plane, BMP)——之外,額外規劃了第 1 到第 16 輔助平面(Supplementary Planes),總共 17 個平面(plane),每個平面都可以放入 65,536 個字符,將字符由原訂的 16 bit 表示法改為 21 bit,至多可以容納 1,114,112 個字符——藉此來收錄那些或許你曾聽過的、也或許沒聽過,目前人類正在使用的、或是曾經在歷史上被使用過的文字。
在經過 30 餘年的搜羅後,在 2023 年 9 月公告(也就是這個月!)的最新版 Unicode 15.1 中,已經收錄了多達 161 種書寫文字,其中包含 94 種當代文字、以及 67 種歷史曾使用的文字,共 149,813 個字符。
Unicode 習慣透過十六進位的方式表示對應的碼位,並帶有 U+
的前綴。
在 BMP 上的字符對應編號為 U+0000
到 U+FFFF
。而為了向下兼容 ASCII,前 128 個碼位與 ASCII 相同。基本上,我們日常生活中會看到的文字,像是拉丁文字、阿拉伯數字、符號、常見漢字、日文假名、韓文、希臘字母、西里爾字母都是放在這一區。
而輔助平面則對應 U+10000
到 U+10FFFF
。其中第一輔助平面除了用來放置了一些已經消亡的古代字母(像是楔形文字、埃及聖書體、西夏文),比較特別的是 Emoji 也被放在這一區。
第二與第三輔助平面基本上都是一些極為罕見的漢字,提交源頭基本上都是姓名罕用字、地名罕用字、宗教用字(佛經、道教、或神道教經書)、甲骨文隸定字、或是古典裡的訛字。
第四到第十三輔助平面目前都是空的(好消息是至少還有這麼多的空間),第十四輔助平面則是用來放置控制字元。
考慮到 Unicode 已經明定不會收錄人造或虛構的文字,像是《魔戒( The Lord of the Rings )》裡的談格瓦(Tengwar)字母、《星艦迷航記( Star Trek )》的克林貢(Klingon)字母、流傳於文藝復興時期歐洲的底比斯(Theban)女巫字母就不在 Unicode 的收錄範圍等。
但這些「文字」,在學術界或是同好圈子裡,仍然會有需要「交換」的時機呀。因此,Unicode 將 BMP 後半部的 U+E000
到 U+F8FF
(共 6,400 個)、以及第十五、十六輔助平面(分別為 U+F0000
- U+FFFFF
,U+100000
- U+10FFFF
,各 65,534 個)定為「私人使用區(Private Use Area, PUA)」。
PUA 裡的碼位都有定義編號,但該長成什麼樣子可以由使用者、組織、或是國家自行規劃。
舉例來說,愛沙尼亞語言研究院(Eesti Keele Instituut,EKI)便曾經使用該區放置那些還沒有被納入標準的衍生拉丁字符,例如在 U+E00A
放置 M with Cedilla
;台灣的教育部萌典也曾將 U+FF545
定義為客語用字 𱱿
(⿰皮卜,該字後來被正式收錄到 Unicode Ext-H,碼位為 U+31C7F
)。
Unicode 的碼位順序為排序帶來了一定程度的方便,舉例來説,想要讓幾個詞按照英文字母的順序,只要由前往後,逐字比較 Unicode 碼位大小即可,但如果有擴充拉丁字母怎麼辦?
許多程式語言內建的 sort()
函數其實都是基於 Unicode 碼位的順序,也因此常常會出現變音符號順序在後的問題,例如:
# python
words = ['cafeteria', 'caffeine', 'café', 'cafe']
words.sort()
print(words)
> ['cafe', 'cafeteria', 'caffeine', 'café'] # 實際輸出
> ['cafe', 'café', 'cafeteria', 'caffeine'] # 理想輸出
caff 居然會比 café 還要更前面——因為 f
(U+0066
) 比 é
(U+00E9
) 還要前面,但在法文裡,é
不被當成獨立字母看待,而是 e
加上尖音符,所以位置應該要和 e
相同位階。
另外一個子是,在英文中 n
的後面接的是 o
,但在西班牙文中,n
的後面是 ñ
(西班牙語就是 español),再來才是 o
。如果直接排序,因為 ñ
(U+00F1
) 的碼位在 n
(U+006E
) 跟 o
(U+006F
) 的後面,所以位置會被放到最後面。
# python
# pintar = 畫畫 / piocha = 鋤頭 / piña = 鳳梨
words = ['piocha', 'pintar', 'piña']
words.sort()
print(words)
> ['pintar', 'piocha', 'piña'] # 實際輸出
> ['pintar', 'piña', 'piocha'] # 理想輸出
對於某些有跨國客群的應用程式,這部分的順序要特別注意。
最後,讓我們來看看 Unicode 的設計原則:
其中的 Characters, not glyphs、Unification、Dynamic composition 與 Stability 原則至關重要,我們在接下來會陸續提到他們。